﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;

namespace ExpressionTrees
{
    class Program
    {
        static void Main(string[] args)
        {
            SimpleAddition();
            Factorial();
            ExpressionFactorial();
        }

        private static void SimpleAddition()
        {
            var operand1 = Expression.Constant(7);
            var operand2 = Expression.Constant(8);
            var resultExpression = Expression.Add(operand1, operand2);

            var func = Expression.Lambda<Func<int>>(resultExpression, null).Compile();
            Console.WriteLine(func());
        }

        private static void Factorial()
        {
            int factorialValue = 7;
            int result = 1;

            for (int i = 1; i < factorialValue; i++)
            {
                result += i * result;
            }

            Console.WriteLine(result);
        }

        private static void ExpressionFactorial()
        {
            ParameterExpression value = Expression.Parameter(typeof(int), "value");

            // Creating an expression to hold a local variable. 
            ParameterExpression result = Expression.Parameter(typeof(int), "result");

            // Creating a label to jump to from a loop.
            LabelTarget label = Expression.Label(typeof(int));

            // Creating a method body.
            BlockExpression block = Expression.Block(
                new[] { result },
                Expression.Assign(result, Expression.Constant(1)),
                    Expression.Loop(
                       Expression.IfThenElse(
                           Expression.GreaterThan(value, Expression.Constant(1)),
                           Expression.MultiplyAssign(result,
                               Expression.PostDecrementAssign(value)),
                           Expression.Break(label, result)
                       ),
                   label
                )
            );

            // Compile and run an expression tree.
            int factorial = Expression.Lambda<Func<int, int>>(block, value).Compile()(7);
            Console.WriteLine(factorial);
        }
    }
}
